﻿using System;
using System.Runtime.CompilerServices;

namespace BattleCore.FixedPoint
{
    /// <summary>
    /// A vector structure.
    /// </summary>
    [Serializable]
    public struct fp3
    {
        private static fp ZeroEpsilonSq = fpmath.Epsilon;
        internal static fp3 InternalZero;
        internal static fp3 Arbitrary;

        /// <summary>The X component of the vector.</summary>
        public fp x;

        /// <summary>The Y component of the vector.</summary>
        public fp y;

        /// <summary>The Z component of the vector.</summary>
        public fp z;

        #region Static readonly variables

        /// <summary>
        /// A vector with components (0,0,0);
        /// </summary>
        public static readonly fp3 zero;

        /// <summary>
        /// A vector with components (-1,0,0);
        /// </summary>
        public static readonly fp3 left;

        /// <summary>
        /// A vector with components (1,0,0);
        /// </summary>
        public static readonly fp3 right;

        /// <summary>
        /// A vector with components (0,1,0);
        /// </summary>
        public static readonly fp3 up;

        /// <summary>
        /// A vector with components (0,-1,0);
        /// </summary>
        public static readonly fp3 down;

        /// <summary>
        /// A vector with components (0,0,-1);
        /// </summary>
        public static readonly fp3 back;

        /// <summary>
        /// A vector with components (0,0,1);
        /// </summary>
        public static readonly fp3 forward;

        /// <summary>
        /// A vector with components (1,1,1);
        /// </summary>
        public static readonly fp3 one;

        /// <summary>
        /// A vector with components 
        /// (FP.MinValue,FP.MinValue,FP.MinValue);
        /// </summary>
        public static readonly fp3 MinValue;

        /// <summary>
        /// A vector with components 
        /// (FP.MaxValue,FP.MaxValue,FP.MaxValue);
        /// </summary>
        public static readonly fp3 MaxValue;

        #endregion

        #region Private static constructor

        static fp3()
        {
            one = new fp3(1, 1, 1);
            zero = new fp3(0, 0, 0);
            left = new fp3(-1, 0, 0);
            right = new fp3(1, 0, 0);
            up = new fp3(0, 1, 0);
            down = new fp3(0, -1, 0);
            back = new fp3(0, 0, -1);
            forward = new fp3(0, 0, 1);
            MinValue = new fp3(fp.MinValue);
            MaxValue = new fp3(fp.MaxValue);
            Arbitrary = new fp3(1, 1, 1);
            InternalZero = zero;
        }

        #endregion

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static fp3 Abs(fp3 other)
        {
            return new fp3(fp.Abs(other.x), fp.Abs(other.y),
                fp.Abs(other.z));
        }

        /// <summary>
        /// Projects a vector onto another vector.
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static fp3 Project(fp3 vector, fp3 onNormal)
        {
            var sqrMag = Dot(onNormal, onNormal);
            if (sqrMag < fpmath.Epsilon)
                return zero;
            else
            {
                var dot = Dot(vector, onNormal) / sqrMag;
                return new fp3(onNormal.x * dot,
                    onNormal.y * dot,
                    onNormal.z * dot);
            }
        }

        /// <summary>
        /// Gets the squared length of the vector.
        /// </summary>
        /// <returns>Returns the squared length of the vector.</returns>
        public fp sqrMagnitude
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get => (((this.x * this.x) + (this.y * this.y)) + (this.z * this.z));
        }

        public fp sqrMagnitudeXZ
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get => ((this.x * this.x) + (this.z * this.z));
        }

        /// <summary>
        /// Gets the length of the vector.
        /// </summary>
        /// <returns>Returns the length of the vector.</returns>
        public fp magnitude
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get
            {
                var num = ((this.x * this.x) + (this.y * this.y)) + (this.z * this.z);
                return fp.Sqrt(num);
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static fp3 ClampMagnitude(fp3 vector, fp maxLength)
        {
            return Normalize(vector) * maxLength;
        }

        /// <summary>
        /// Gets a normalized version of the vector.
        /// </summary>
        /// <returns>Returns a normalized version of the vector.</returns>
        public fp3 normalized
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get
            {
                var result = new fp3(this.x, this.y, this.z);
                result.Normalize();
                return result;
            }
        }

        /// <summary>
        /// Constructor initializing a new instance of the structure
        /// </summary>
        /// <param name="x">The X component of the vector.</param>
        /// <param name="y">The Y component of the vector.</param>
        /// <param name="z">The Z component of the vector.</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public fp3(int x, int y, int z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        //[MethodImpl(MethodImplOptions.AggressiveInlining)]
        //public fp3(Vector3 vector3)
        //{
        //    x = vector3.x;
        //    y = vector3.y;
        //    z = vector3.z;
        //}

        //[MethodImpl(MethodImplOptions.AggressiveInlining)]
        //public fp3(Vector3Int vector3)
        //{
        //    x = vector3.x;
        //    y = vector3.y;
        //    z = vector3.z;
        //}

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public fp3(fp x, fp y, fp z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        /// <summary>
        /// Multiplies each component of the vector by the same components of the provided vector.
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Scale(fp3 other)
        {
            this.x = x * other.x;
            this.y = y * other.y;
            this.z = z * other.z;
        }

        /// <summary>
        /// Sets all vector component to specific values.
        /// </summary>
        /// <param name="x">The X component of the vector.</param>
        /// <param name="y">The Y component of the vector.</param>
        /// <param name="z">The Z component of the vector.</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Set(fp x, fp y, fp z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        /// <summary>
        /// Constructor initializing a new instance of the structure
        /// </summary>
        /// <param name="xyz">All components of the vector are set to xyz</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public fp3(fp xyz)
        {
            this.x = xyz;
            this.y = xyz;
            this.z = xyz;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static fp3 Lerp(fp3 from, fp3 to, fp percent)
        {
            return from + (to - from) * percent;
        }

        /// <summary>
        /// Builds a string from the JVector.
        /// </summary>
        /// <returns>A string containing all three components.</returns>

        #region public override string ToString()

        public override string ToString()
        {
            return $"({x.AsFloat():f1}, {y.AsFloat():f1}, {z.AsFloat():f1})";
        }

        #endregion

        /// <summary>
        /// Tests if an object is equal to this vector.
        /// </summary>
        /// <param name="obj">The object to test.</param>
        /// <returns>Returns true if they are equal, otherwise false.</returns>

        #region public override bool Equals(object obj)

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public override bool Equals(object obj)
        {
            if (!(obj is fp3)) return false;
            var other = (fp3)obj;

            return (((x == other.x) && (y == other.y)) && (z == other.z));
        }

        #endregion

        /// <summary>
        /// Multiplies each component of the vector by the same components of the provided vector.
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static fp3 Scale(fp3 vecA, fp3 vecB)
        {
            fp3 result;
            result.x = vecA.x * vecB.x;
            result.y = vecA.y * vecB.y;
            result.z = vecA.z * vecB.z;

            return result;
        }

        /// <summary>
        /// Tests if two JVector are equal.
        /// </summary>
        /// <param name="value1">The first value.</param>
        /// <param name="value2">The second value.</param>
        /// <returns>Returns true if both values are equal, otherwise false.</returns>

        #region public static bool operator ==(JVector value1, JVector value2)

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool operator ==(fp3 value1, fp3 value2)
        {
            return (((value1.x == value2.x) && (value1.y == value2.y)) && (value1.z == value2.z));
        }

        #endregion

        /// <summary>
        /// Tests if two JVector are not equal.
        /// </summary>
        /// <param name="value1">The first value.</param>
        /// <param name="value2">The second value.</param>
        /// <returns>Returns false if both values are equal, otherwise true.</returns>

        #region public static bool operator !=(JVector value1, JVector value2)

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool operator !=(fp3 value1, fp3 value2)
        {
            if ((value1.x == value2.x) && (value1.y == value2.y))
            {
                return (value1.z != value2.z);
            }

            return true;
        }

        #endregion

        /// <summary>
        /// Gets a vector with the minimum x,y and z values of both vectors.
        /// </summary>
        /// <param name="value1">The first value.</param>
        /// <param name="value2">The second value.</param>
        /// <returns>A vector with the minimum x,y and z values of both vectors.</returns>

        #region public static JVector Min(JVector value1, JVector value2)

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static fp3 Min(fp3 value1, fp3 value2)
        {
            fp3.Min(ref value1, ref value2, out var result);
            return result;
        }

        /// <summary>
        /// Gets a vector with the minimum x,y and z values of both vectors.
        /// </summary>
        /// <param name="value1">The first value.</param>
        /// <param name="value2">The second value.</param>
        /// <param name="result">A vector with the minimum x,y and z values of both vectors.</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void Min(ref fp3 value1, ref fp3 value2, out fp3 result)
        {
            result.x = (value1.x < value2.x) ? value1.x : value2.x;
            result.y = (value1.y < value2.y) ? value1.y : value2.y;
            result.z = (value1.z < value2.z) ? value1.z : value2.z;
        }

        #endregion

        /// <summary>
        /// Gets a vector with the maximum x,y and z values of both vectors.
        /// </summary>
        /// <param name="value1">The first value.</param>
        /// <param name="value2">The second value.</param>
        /// <returns>A vector with the maximum x,y and z values of both vectors.</returns>

        #region public static JVector Max(JVector value1, JVector value2)

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static fp3 Max(fp3 value1, fp3 value2)
        {
            fp3.Max(ref value1, ref value2, out var result);
            return result;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static fp Distance(fp3 v1, fp3 v2)
        {
            return fp.Sqrt((v1.x - v2.x) * (v1.x - v2.x) + (v1.y - v2.y) * (v1.y - v2.y) +
                           (v1.z - v2.z) * (v1.z - v2.z));
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static fp DistanceXZ(fp3 v1, fp3 v2)
        {
            return fp.Sqrt((v1.x - v2.x) * (v1.x - v2.x) + (v1.z - v2.z) * (v1.z - v2.z));
        }

        /// <summary>
        /// Gets a vector with the maximum x,y and z values of both vectors.
        /// </summary>
        /// <param name="value1">The first value.</param>
        /// <param name="value2">The second value.</param>
        /// <param name="result">A vector with the maximum x,y and z values of both vectors.</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void Max(ref fp3 value1, ref fp3 value2, out fp3 result)
        {
            result.x = (value1.x > value2.x) ? value1.x : value2.x;
            result.y = (value1.y > value2.y) ? value1.y : value2.y;
            result.z = (value1.z > value2.z) ? value1.z : value2.z;
        }

        #endregion

        /// <summary>
        /// Sets the length of the vector to zero.
        /// </summary>

        #region public void MakeZero()

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void MakeZero()
        {
            x = fp.Zero;
            y = fp.Zero;
            z = fp.Zero;
        }

        #endregion

        /// <summary>
        /// Checks if the length of the vector is zero.
        /// </summary>
        /// <returns>Returns true if the vector is zero, otherwise false.</returns>

        #region public bool IsZero()

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool IsZero()
        {
            return (this.sqrMagnitude == fp.Zero);
        }

        /// <summary>
        /// Checks if the length of the vector is nearly zero.
        /// </summary>
        /// <returns>Returns true if the vector is nearly zero, otherwise false.</returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool IsNearlyZero()
        {
            return (this.sqrMagnitude < ZeroEpsilonSq);
        }

        #endregion

        /// <summary>
        /// Transforms a vector by the given matrix.
        /// </summary>
        /// <param name="position">The vector to transform.</param>
        /// <param name="matrix">The transform matrix.</param>
        /// <returns>The transformed vector.</returns>

        #region public static JVector Transform(JVector position, JMatrix matrix)

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static fp3 Transform(fp3 position, fpmatrix matrix)
        {
            fp3.Transform(ref position, ref matrix, out var result);
            return result;
        }

        /// <summary>
        /// Transforms a vector by the given matrix.
        /// </summary>
        /// <param name="position">The vector to transform.</param>
        /// <param name="matrix">The transform matrix.</param>
        /// <param name="result">The transformed vector.</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void Transform(ref fp3 position, ref fpmatrix matrix,
            out fp3 result)
        {
            var num0 = ((position.x * matrix.M11) + (position.y * matrix.M21)) + (position.z * matrix.M31);
            var num1 = ((position.x * matrix.M12) + (position.y * matrix.M22)) + (position.z * matrix.M32);
            var num2 = ((position.x * matrix.M13) + (position.y * matrix.M23)) + (position.z * matrix.M33);

            result.x = num0;
            result.y = num1;
            result.z = num2;
        }

        /// <summary>
        /// Transforms a vector by the transposed of the given Matrix.
        /// </summary>
        /// <param name="position">The vector to transform.</param>
        /// <param name="matrix">The transform matrix.</param>
        /// <param name="result">The transformed vector.</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void TransposedTransform(ref fp3 position, ref fpmatrix matrix,
            out fp3 result)
        {
            var num0 = ((position.x * matrix.M11) + (position.y * matrix.M12)) + (position.z * matrix.M13);
            var num1 = ((position.x * matrix.M21) + (position.y * matrix.M22)) + (position.z * matrix.M23);
            var num2 = ((position.x * matrix.M31) + (position.y * matrix.M32)) + (position.z * matrix.M33);

            result.x = num0;
            result.y = num1;
            result.z = num2;
        }

        #endregion

        /// <summary>
        /// Calculates the dot product of two vectors.
        /// </summary>
        /// <param name="vector1">The first vector.</param>
        /// <param name="vector2">The second vector.</param>
        /// <returns>Returns the dot product of both vectors.</returns>

        #region public static FP Dot(JVector vector1, JVector vector2)

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static fp Dot(fp3 vector1, fp3 vector2)
        {
            return fp3.Dot(ref vector1, ref vector2);
        }

        /// <summary>
        /// Calculates the dot product of both vectors.
        /// </summary>
        /// <param name="vector1">The first vector.</param>
        /// <param name="vector2">The second vector.</param>
        /// <returns>Returns the dot product of both vectors.</returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static fp Dot(ref fp3 vector1, ref fp3 vector2)
        {
            return ((vector1.x * vector2.x) + (vector1.y * vector2.y)) + (vector1.z * vector2.z);
        }

        #endregion

        /// <summary>
        /// Adds two vectors.
        /// </summary>
        /// <param name="value1">The first vector.</param>
        /// <param name="value2">The second vector.</param>
        /// <returns>The sum of both vectors.</returns>

        #region public static void Add(JVector value1, JVector value2)

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static fp3 Add(fp3 value1, fp3 value2)
        {
            fp3.Add(ref value1, ref value2, out var result);
            return result;
        }

        /// <summary>
        /// Adds to vectors.
        /// </summary>
        /// <param name="value1">The first vector.</param>
        /// <param name="value2">The second vector.</param>
        /// <param name="result">The sum of both vectors.</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void Add(ref fp3 value1, ref fp3 value2, out fp3 result)
        {
            fp num0 = value1.x + value2.x;
            fp num1 = value1.y + value2.y;
            fp num2 = value1.z + value2.z;

            result.x = num0;
            result.y = num1;
            result.z = num2;
        }

        #endregion

        /// <summary>
        /// Divides a vector by a factor.
        /// </summary>
        /// <param name="value1">The vector to divide.</param>
        /// <param name="scaleFactor">The scale factor.</param>
        /// <returns>Returns the scaled vector.</returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static fp3 Divide(fp3 value1, fp scaleFactor)
        {
            fp3.Divide(ref value1, scaleFactor, out var result);
            return result;
        }

        /// <summary>
        /// Divides a vector by a factor.
        /// </summary>
        /// <param name="value1">The vector to divide.</param>
        /// <param name="scaleFactor">The scale factor.</param>
        /// <param name="result">Returns the scaled vector.</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void Divide(ref fp3 value1, fp scaleFactor, out fp3 result)
        {
            result.x = value1.x / scaleFactor;
            result.y = value1.y / scaleFactor;
            result.z = value1.z / scaleFactor;
        }

        /// <summary>
        /// Subtracts two vectors.
        /// </summary>
        /// <param name="value1">The first vector.</param>
        /// <param name="value2">The second vector.</param>
        /// <returns>The difference of both vectors.</returns>

        #region public static JVector Subtract(JVector value1, JVector value2)

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static fp3 Subtract(fp3 value1, fp3 value2)
        {
            fp3.Subtract(ref value1, ref value2, out var result);
            return result;
        }

        /// <summary>
        /// Subtracts to vectors.
        /// </summary>
        /// <param name="value1">The first vector.</param>
        /// <param name="value2">The second vector.</param>
        /// <param name="result">The difference of both vectors.</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void Subtract(ref fp3 value1, ref fp3 value2,
            out fp3 result)
        {
            fp num0 = value1.x - value2.x;
            fp num1 = value1.y - value2.y;
            fp num2 = value1.z - value2.z;

            result.x = num0;
            result.y = num1;
            result.z = num2;
        }

        #endregion

        /// <summary>
        /// The cross product of two vectors.
        /// </summary>
        /// <param name="vector1">The first vector.</param>
        /// <param name="vector2">The second vector.</param>
        /// <returns>The cross product of both vectors.</returns>

        #region public static JVector Cross(JVector vector1, JVector vector2)

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static fp3 Cross(fp3 vector1, fp3 vector2)
        {
            fp3.Cross(ref vector1, ref vector2, out var result);
            return result;
        }

        /// <summary>
        /// The cross product of two vectors.
        /// </summary>
        /// <param name="vector1">The first vector.</param>
        /// <param name="vector2">The second vector.</param>
        /// <param name="result">The cross product of both vectors.</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void Cross(ref fp3 vector1, ref fp3 vector2,
            out fp3 result)
        {
            fp num3 = (vector1.y * vector2.z) - (vector1.z * vector2.y);
            fp num2 = (vector1.z * vector2.x) - (vector1.x * vector2.z);
            fp num = (vector1.x * vector2.y) - (vector1.y * vector2.x);
            result.x = num3;
            result.y = num2;
            result.z = num;
        }

        #endregion

        /// <summary>
        /// Gets the hashcode of the vector.
        /// </summary>
        /// <returns>Returns the hashcode of the vector.</returns>

        #region public override int GetHashCode()

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public override int GetHashCode()
        {
            return x.GetHashCode() ^ y.GetHashCode() ^ z.GetHashCode();
        }

        #endregion

        /// <summary>
        /// Inverses the direction of the vector.
        /// </summary>

        #region public static JVector Negate(JVector value)

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Negate()
        {
            this.x = -this.x;
            this.y = -this.y;
            this.z = -this.z;
        }

        /// <summary>
        /// Inverses the direction of a vector.
        /// </summary>
        /// <param name="value">The vector to inverse.</param>
        /// <returns>The negated vector.</returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static fp3 Negate(fp3 value)
        {
            fp3.Negate(ref value, out var result);
            return result;
        }

        /// <summary>
        /// Inverses the direction of a vector.
        /// </summary>
        /// <param name="value">The vector to inverse.</param>
        /// <param name="result">The negated vector.</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void Negate(ref fp3 value, out fp3 result)
        {
            var num0 = -value.x;
            var num1 = -value.y;
            var num2 = -value.z;

            result.x = num0;
            result.y = num1;
            result.z = num2;
        }

        #endregion

        /// <summary>
        /// Normalizes the given vector.
        /// </summary>
        /// <param name="value">The vector which should be normalized.</param>
        /// <returns>A normalized vector.</returns>

        #region public static JVector Normalize(JVector value)

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static fp3 Normalize(fp3 value)
        {
            fp3.Normalize(ref value, out var result);
            return result;
        }

        /// <summary>
        /// Normalizes this vector.
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Normalize()
        {
            var num2 = ((this.x * this.x) + (this.y * this.y)) + (this.z * this.z);
            var num = fp.One / fp.Sqrt(num2);
            this.x *= num;
            this.y *= num;
            this.z *= num;
        }

        /// <summary>
        /// Normalizes the given vector.
        /// </summary>
        /// <param name="value">The vector which should be normalized.</param>
        /// <param name="result">A normalized vector.</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void Normalize(ref fp3 value, out fp3 result)
        {
            var num2 = ((value.x * value.x) + (value.y * value.y)) + (value.z * value.z);
            var num = fp.One / fp.Sqrt(num2);
            result.x = value.x * num;
            result.y = value.y * num;
            result.z = value.z * num;
        }

        #endregion

        #region public static void Swap(ref JVector vector1, ref JVector vector2)

        /// <summary>
        /// Swaps the components of both vectors.
        /// </summary>
        /// <param name="vector1">The first vector to swap with the second.</param>
        /// <param name="vector2">The second vector to swap with the first.</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void Swap(ref fp3 vector1, ref fp3 vector2)
        {
            var temp = vector1.x;
            vector1.x = vector2.x;
            vector2.x = temp;

            temp = vector1.y;
            vector1.y = vector2.y;
            vector2.y = temp;

            temp = vector1.z;
            vector1.z = vector2.z;
            vector2.z = temp;
        }

        #endregion

        /// <summary>
        /// Multiply a vector with a factor.
        /// </summary>
        /// <param name="value1">The vector to multiply.</param>
        /// <param name="scaleFactor">The scale factor.</param>
        /// <returns>Returns the multiplied vector.</returns>

        #region public static JVector Multiply(JVector value1, FP scaleFactor)

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static fp3 Multiply(fp3 value1, fp scaleFactor)
        {
            Multiply(ref value1, scaleFactor, out var result);
            return result;
        }

        /// <summary>
        /// Multiply a vector with a factor.
        /// </summary>
        /// <param name="value1">The vector to multiply.</param>
        /// <param name="scaleFactor">The scale factor.</param>
        /// <returns>Returns the multiplied vector.</returns>
        //[MethodImpl(MethodImplOptions.AggressiveInlining)]
        //public static fp3 Multiply(Vector3Int value1, fp scaleFactor)
        //{
        //    var fixedPoint = new fp3(value1);
        //    Multiply(ref fixedPoint, scaleFactor, out var result);
        //    return result;
        //}

        /// <summary>
        /// Multiply a vector with a factor.
        /// </summary>
        /// <param name="value1">The vector to multiply.</param>
        /// <param name="scaleFactor">The scale factor.</param>
        /// <param name="result">Returns the multiplied vector.</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void Multiply(ref fp3 value1, fp scaleFactor,
            out fp3 result)
        {
            result.x = value1.x * scaleFactor;
            result.y = value1.y * scaleFactor;
            result.z = value1.z * scaleFactor;
        }

        #endregion

        /// <summary>
        /// Calculates the cross product of two vectors.
        /// </summary>
        /// <param name="value1">The first vector.</param>
        /// <param name="value2">The second vector.</param>
        /// <returns>Returns the cross product of both.</returns>

        #region public static JVector operator %(JVector value1, JVector value2)

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static fp3 operator %(fp3 value1, fp3 value2)
        {
            fp3.Cross(ref value1, ref value2, out var result);
            return result;
        }

        #endregion

        /// <summary>
        /// Calculates the dot product of two vectors.
        /// </summary>
        /// <param name="value1">The first vector.</param>
        /// <param name="value2">The second vector.</param>
        /// <returns>Returns the dot product of both.</returns>

        #region public static FP operator *(JVector value1, JVector value2)

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static fp operator *(fp3 value1, fp3 value2)
        {
            return fp3.Dot(ref value1, ref value2);
        }

        #endregion

        /// <summary>
        /// Multiplies a vector by a scale factor.
        /// </summary>
        /// <param name="value1">The vector to scale.</param>
        /// <param name="value2">The scale factor.</param>
        /// <returns>Returns the scaled vector.</returns>

        #region public static JVector operator *(JVector value1, FP value2)

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static fp3 operator *(fp3 value1, fp value2)
        {
            fp3.Multiply(ref value1, value2, out var result);
            return result;
        }

        #endregion

        /// <summary>
        /// Multiplies a vector by a scale factor.
        /// </summary>
        /// <param name="value2">The vector to scale.</param>
        /// <param name="value1">The scale factor.</param>
        /// <returns>Returns the scaled vector.</returns>

        #region public static JVector operator *(FP value1, JVector value2)

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static fp3 operator *(fp value1, fp3 value2)
        {
            fp3.Multiply(ref value2, value1, out var result);
            return result;
        }

        #endregion

        /// <summary>
        /// Subtracts two vectors.
        /// </summary>
        /// <param name="value1">The first vector.</param>
        /// <param name="value2">The second vector.</param>
        /// <returns>The difference of both vectors.</returns>

        #region public static JVector operator -(JVector value1, JVector value2)

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static fp3 operator -(fp3 value1, fp3 value2)
        {
            fp3.Subtract(ref value1, ref value2, out var result);
            return result;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static fp3 operator -(fp3 value)
        {
            return new fp3(-value.x, -value.y, -value.z);
        }

        #endregion

        /// <summary>
        /// Adds two vectors.
        /// </summary>
        /// <param name="value1">The first vector.</param>
        /// <param name="value2">The second vector.</param>
        /// <returns>The sum of both vectors.</returns>

        #region public static JVector operator +(JVector value1, JVector value2)

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static fp3 operator +(fp3 value1, fp3 value2)
        {
            fp3.Add(ref value1, ref value2, out var result);
            return result;
        }

        #endregion

        /// <summary>
        /// Divides a vector by a factor.
        /// </summary>
        /// <param name="value1">The vector to divide.</param>
        /// <param name="value2">The scale factor.</param>
        /// <returns>Returns the scaled vector.</returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static fp3 operator /(fp3 value1, fp value2)
        {
            Divide(ref value1, value2, out var result);
            return result;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static fp Angle(fp3 a, fp3 b)
        {
            return fp.Acos(a.normalized * b.normalized) * fp.Rad2Deg;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public fp2 ToTSVector2()
        {
            return new fp2(x, y);
        }

        //[MethodImpl(MethodImplOptions.AggressiveInlining)]
        //public Vector3 ToVector3()
        //{
        //    return new Vector3(x.AsFloat(), y.AsFloat(), z.AsFloat());
        //}

        //[MethodImpl(MethodImplOptions.AggressiveInlining)]
        //public Vector3Int ToVector3Int()
        //{
        //    return new Vector3Int(x.AsInt(), y.AsInt(), z.AsInt());
        //}
    }
}